%include "boot.inc"

org 0x7e00
[bits 16]

;空描述符
mov dword [GDT_START_ADDRESS+0x00],0x00
mov dword [GDT_START_ADDRESS+0x04],0x00  

;创建描述符，这是一个数据段，对应0~4GB的线性地址空间
mov dword [GDT_START_ADDRESS+0x08],0x0000ffff    ; 基地址为0，段界限为0xFFFFF
mov dword [GDT_START_ADDRESS+0x0c],0x00cf9200    ; 粒度为4KB，存储器段描述符 

;建立保护模式下的堆栈段描述符      
mov dword [GDT_START_ADDRESS+0x10],0x00000000    ; 基地址为0x00000000，界限0x0 
mov dword [GDT_START_ADDRESS+0x14],0x00409600    ; 粒度为1个字节

;建立保护模式下的显存描述符   
mov dword [GDT_START_ADDRESS+0x18],0x80007fff    ; 基地址为0x000B8000，界限0x07FFF 
mov dword [GDT_START_ADDRESS+0x1c],0x0040920b    ; 粒度为字节

;创建保护模式下平坦模式代码段描述符
mov dword [GDT_START_ADDRESS+0x20],0x0000ffff    ; 基地址为0，段界限为0xFFFFF
mov dword [GDT_START_ADDRESS+0x24],0x00cf9800    ; 粒度为4kb，代码段描述符 

;初始化描述符表寄存器GDTR
mov word [pgdt], 39      ;描述符表的界限   
lgdt [pgdt]
      
in al,0x92                         ;南桥芯片内的端口 
or al,0000_0010B
out 0x92,al                        ;打开A20

cli                                ;中断机制尚未工作
mov eax,cr0
or eax,1
mov cr0,eax                        ;设置PE位
      
;以下进入保护模式
jmp dword CODE_SELECTOR:protect_mode_begin

;16位的描述符选择子：32位偏移
;清流水线并串行化处理器
[bits 32]           
protect_mode_begin:                              

;定义各个符号
    _dr equ 1
    _ur equ 2
    _ul equ 3
    _dl equ 4
    delay equ 1000
    ddelay equ 500

begin:
    mov eax, DATA_SELECTOR
    mov ds, eax
    mov gs, eax
    mov eax, STACK_SELECTOR
    mov ss, eax
    mov eax, VIDEO_SELECTOR
    mov es, eax

;循环实现延迟
LOOP1:
    dec word[count]
    jnz LOOP1
    
    mov word[count], delay
    dec word[dcount]
    jnz LOOP1
    
    mov word[count], delay
    mov word[dcount], ddelay
   
    mov al,1
        cmp al, byte[rdul]
    jz DnRt
      
    mov al, 2
          cmp al, byte[rdul]
    jz UpRt

          mov al, 3
          cmp al, byte[rdul]
    jz UpLt
      
    mov al, 4
          cmp al, byte[rdul]
    jz  DnLt

    jmp $

;往右下移动，判断是否碰壁并显示字符
DnRt:
    inc word[x]
    inc word[y]
    mov bx, word[x]
    mov ax, 25
    sub ax, bx
         jz  dr2ur
    
    mov bx, word[y]
    mov ax, 80
    sub ax, bx
          jz  dr2dl
    
    jmp show

dr2ur:
          mov word[x], 23
          mov byte[rdul], _ur    
          jmp show
dr2dl:
          mov word[y], 78
         mov byte[rdul], _dl    
          jmp show

;往右上移动，判断是否碰壁并显示字符
UpRt:
    dec word[x]
    inc word[y]
    mov bx, word[y]
    mov ax, 80
    sub ax, bx
          jz  ur2ul
    
    mov bx, word[x]
    mov ax, 0
    sub ax, bx
         jz  ur2dr
    
    jmp show

ur2ul:
          mov word[y], 78
          mov byte[rdul], _ul    
          jmp show
ur2dr:
          mov word[x], 1
          mov byte[rdul], _dr    
          jmp show
    
;往左上移动，判断是否碰壁并显示字符
UpLt:
    dec word[x]
    dec word[y]
    mov bx, word[x]
    mov ax, 0
    sub ax, bx
          jz  ul2dl

    mov bx,word[y]
    mov ax, -1
    sub ax, bx
         jz  ul2ur
    
    jmp show

ul2dl:
          mov word[x], 1
          mov byte[rdul], _dl    
          jmp show
ul2ur:
          mov word[y], 1
          mov byte[rdul], _ur    
          jmp show

;往左下移动，判断是否碰壁并显示字符
DnLt:
    inc word[x]
    dec word[y]
    mov bx, word[y]
    mov ax, -1
    sub ax, bx
          jz  dl2dr
    
    mov bx, word[x]
    mov ax, 25
    sub ax, bx
          jz  dl2ul
    
    jmp show

dl2dr:
          mov word[y], 1
          mov byte[rdul], _dr    
          jmp show
dl2ul:
          mov word[x], 23
         mov byte[rdul], _ul    
          jmp show

;在屏幕上显示字符
show:    
    xor ax, ax           
    mov ax, word[x]
    mov bx, 80
    mul bx
    add ax, word[y]
    mov bx, 2
    mul bx
    mov bx, ax
    mov ah, byte[color]      
    mov al, byte[char]    
    mov [es:bx], ax  
    
    inc byte[char]
    cmp byte[char], 'z'+1
    jnz keep
    mov byte[char], '0'

keep:    
    inc byte[color]
    cmp byte[color], 0x10
    jnz LOOP1
    mov byte[color], 0x40 ;    循环显示不同样式的字符
    jmp LOOP1

end:
    jmp $

    count dw delay        ;一层延迟
    dcount dw ddelay    ;二层延迟
    rdul db _dr        ;方向变量
    color db 0x02        ;样式（颜色）变量
    x dw 0                ;横坐标
    y dw 0                ;纵坐标
    char db 'A'            ;要显示的字符

pgdt dw 0
     dd GDT_START_ADDRESS
    
